using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using vimApi;

namespace Alex.Library.Cloud.VMWare.V51.Util
{
    public class VMUtils
    {
        private AppUtil cb;
        private SvcConnection _connection;

        public VMUtils(AppUtil argCB)
        {
            cb = argCB;
        }

        public void Init(AppUtil argCB)
        {
            this.cb = argCB;
            _connection = argCB.SvcConnection;

        }

        public VirtualMachineConfigSpec CreateVmConfigSpec(string vmName, string datastoreName,
            int diskSizeMB, ManagedObjectReference computeResMor, ManagedObjectReference hostMor)
        {

            ConfigTarget configTarget = GetConfigTargetForHost(computeResMor, hostMor);
            VirtualDevice[] defaultDevices = GetDefaultDevices(computeResMor, hostMor);
            VirtualMachineConfigSpec configSpec = new VirtualMachineConfigSpec();

            string networkName = null;
            if (configTarget.network != null)
            {
                foreach (var netInfo in configTarget.network)
                {
                    NetworkSummary netSummary = netInfo.network;
                    if (netSummary.accessible)
                    {
                        networkName = netSummary.name;
                        break;
                    }
                }
            }
            ManagedObjectReference datastoreRef = null;
            if (!String.IsNullOrEmpty(datastoreName))
            {
                bool flag = false;
                foreach (var vdsInfo in configTarget.datastore)
                {
                    DatastoreSummary dsSummary = vdsInfo.datastore;
                    if (dsSummary.name.Equals(datastoreName))
                    {
                        flag = true;
                        if (dsSummary.accessible)
                        {
                            datastoreName = dsSummary.name;
                            datastoreRef = dsSummary.datastore;
                        }
                        else
                        {
                            throw new Exception("Specified Datastore is not accessible");
                        }
                        break;
                    }
                }
                if (!flag)
                {
                    throw new Exception("Specified Datastore is not Found");
                }
            }
            else
            {
                bool flag = false;
                foreach (var vdsInfo in configTarget.datastore)
                {
                    DatastoreSummary dsSummary = vdsInfo.datastore;
                    if (dsSummary.accessible)
                    {
                        datastoreName = dsSummary.name;
                        datastoreRef = dsSummary.datastore;
                        flag = true;
                        break;
                    }
                }
                if (!flag)
                {
                    throw new Exception("No Datastore found on host");
                }
            }
            string datastoreVolume = GetVolumeName(datastoreName);
            VirtualMachineFileInfo vmfi = new VirtualMachineFileInfo();
            vmfi.vmPathName = datastoreVolume;
            configSpec.files = vmfi;
            // Add a scsi controller
            int diskCtlrKey = 1;
            VirtualDeviceConfigSpec scsiCtrlSpec = new VirtualDeviceConfigSpec();
            scsiCtrlSpec.operation = VirtualDeviceConfigSpecOperation.add;
            scsiCtrlSpec.operationSpecified = true;
            VirtualLsiLogicController scsiCtrl = new VirtualLsiLogicController();
            scsiCtrl.busNumber = 0;
            scsiCtrlSpec.device = scsiCtrl;
            scsiCtrl.key = diskCtlrKey;
            scsiCtrl.sharedBus = VirtualSCSISharing.physicalSharing;
            string ctlrType = scsiCtrl.GetType().Name;

            // Find the IDE controller
            VirtualDevice ideCtrl = defaultDevices.SingleOrDefault(ide => ide.GetType().Name.Equals("VirtualIDEController"));

            // Add a floppy
            VirtualDeviceConfigSpec floppySpec = new VirtualDeviceConfigSpec();
            floppySpec.operation = VirtualDeviceConfigSpecOperation.add;
            floppySpec.operationSpecified = true;
            VirtualFloppy floppy = new VirtualFloppy();
            VirtualFloppyDeviceBackingInfo flpBacking = new VirtualFloppyDeviceBackingInfo();
            flpBacking.deviceName = "/dev/fd0";
            floppy.backing = flpBacking;
            floppy.key = 3;
            floppySpec.device = floppy;

            // Add a cd-rom based on a physical device
            VirtualDeviceConfigSpec cdSpec = null;

            if (ideCtrl != null)
            {
                cdSpec = new VirtualDeviceConfigSpec();
                cdSpec.operation = VirtualDeviceConfigSpecOperation.add;
                cdSpec.operationSpecified = true;
                VirtualCdrom cdrom = new VirtualCdrom();
                VirtualCdromIsoBackingInfo cdDeviceBacking = new VirtualCdromIsoBackingInfo();
                cdDeviceBacking.datastore = datastoreRef;
                cdDeviceBacking.fileName = datastoreVolume + "testcd.iso";
                cdrom.backing = cdDeviceBacking;
                cdrom.key = 20;
                cdrom.controllerKey = ideCtrl.key;
                cdrom.controllerKeySpecified = true;
                cdrom.unitNumberSpecified = true;
                cdrom.unitNumber = 0;
                cdSpec.device = cdrom;
            }

            // Create a new disk - file based - for the vm
            VirtualDeviceConfigSpec diskSpec = CreateVirtualDisk(datastoreName, diskCtlrKey,
                datastoreRef, diskSizeMB);

            // Add a NIC. the network Name must be set as the device name to create the NIC.
            VirtualDeviceConfigSpec nicSpec = new VirtualDeviceConfigSpec();
            if (!String.IsNullOrEmpty(networkName))
            {
                nicSpec.operation = VirtualDeviceConfigSpecOperation.add;
                nicSpec.operationSpecified = true;
                VirtualEthernetCard nic = new VirtualPCNet32();
                VirtualEthernetCardNetworkBackingInfo nicBacking = new VirtualEthernetCardNetworkBackingInfo();
                nicBacking.deviceName = networkName;
                nic.addressType = "generated";
                nic.backing = nicBacking;
                nic.key = 4;
                nicSpec.device = nic;
            }

            VirtualDeviceConfigSpec[] deviceConfigSpec = null;
            if (ideCtrl != null)
            {
                deviceConfigSpec = new VirtualDeviceConfigSpec[5];

                deviceConfigSpec[3] = cdSpec;
                deviceConfigSpec[4] = nicSpec;
            }
            else
            {
                deviceConfigSpec = new VirtualDeviceConfigSpec[4];
                deviceConfigSpec[3] = nicSpec;
            }

            deviceConfigSpec[0] = scsiCtrlSpec;
            deviceConfigSpec[1] = floppySpec;
            deviceConfigSpec[2] = diskSpec;

            configSpec.deviceChange = deviceConfigSpec;
            return configSpec;
        }

        /**
        * This method returns the ConfigTarget for a HostSystem
        * @param computeResMor A MoRef to the ComputeResource used by the HostSystem
        * @param hostMor A MoRef to the HostSystem
        * @return Instance of ConfigTarget for the supplied 
        * HostSystem/ComputeResource
        * @throws Exception When no ConfigTarget can be found
        */
        public ConfigTarget GetConfigTargetForHost(ManagedObjectReference computeResMor,
            ManagedObjectReference hostMor)
        {

            ManagedObjectReference envBrowseMor =
               cb.Service.GetMoRefProp(computeResMor, "environmentBrowser");

            ConfigTarget configTarget =
             cb.SvcConnection.Service.QueryConfigTarget(envBrowseMor, hostMor);

            if (configTarget == null)
            {
                throw new Exception("No ConfigTarget found in ComputeResource");
            }

            return configTarget;
        }

        /** 
         * The method returns the default devices from the HostSystem
         * @param computeResMor A MoRef to the ComputeResource used by the HostSystem
         * @param hostMor A MoRef to the HostSystem
         * @return Array of VirtualDevice containing the default devices for 
         * the HostSystem
         * @throws Exception
         */
        public VirtualDevice[] GetDefaultDevices(ManagedObjectReference computeResMor,
            ManagedObjectReference hostMor)
        {
            ManagedObjectReference envBrowseMor =
               cb.Service.GetMoRefProp(computeResMor, "environmentBrowser");

            VirtualMachineConfigOption cfgOpt =
               cb.SvcConnection.Service.QueryConfigOption(envBrowseMor, null, hostMor);

            VirtualDevice[] defaultDevs = null;

            if (cfgOpt == null)
            {
                throw new Exception("No VirtualHardwareInfo found in ComputeResource");
            }
            else
            {
                defaultDevs = cfgOpt.defaultDevice;
                if (defaultDevs == null)
                {
                    throw new Exception("No Datastore found in ComputeResource");
                }
            }

            return defaultDevs;
        }

        private String GetVolumeName(string volName)
        {
            return !String.IsNullOrEmpty(volName) ? "[" + volName + "]" : "[Local]";
        }

        /**
         * This method returns the contents of the hostFolder property from the
         * supplied Datacenter MoRef
         * @param dcmor MoRef to the Datacenter
         * @return MoRef to a Folder returned by the hostFolder property or
         * null if dcmor is NOT a MoRef to a Datacenter or if the hostFolder 
         * doesn't exist
         * @throws Exception
         */
        public ManagedObjectReference GetHostFolder(ManagedObjectReference dcmor)
        {
            return cb.Service.GetMoRefProp(dcmor, "hostFolder");
        }

        /**
        * This method returns a MoRef to the HostSystem with the supplied name 
        * under the supplied Folder. If hostname is null, it returns the first
        * HostSystem found under the supplied Folder
        * @param hostFolderMor MoRef to the Folder to look in
        * @param hostname Name of the HostSystem you are looking for
        * @return MoRef to the HostSystem or null if not found
        * @throws Exception
        */
        public ManagedObjectReference GetHost(ManagedObjectReference hostFolderMor, string hostName)
        {
            ManagedObjectReference hostmor = null;

            if (!String.IsNullOrEmpty(hostName))
            {
                hostmor = cb.Service.GetDecendentMoRef(hostFolderMor, "HostSystem", hostName);
            }
            else
            {
                hostmor = cb.Service.GetFirstDecendentMoRef(hostFolderMor, "HostSystem");
            }

            return hostmor;
        }

        public VirtualDeviceConfigSpec CreateVirtualDisk(string volName, int diskCtlrKey,
            ManagedObjectReference datastoreRef, int diskSizeMB)
        {
            string volumeName = GetVolumeName(volName);
            VirtualDeviceConfigSpec diskSpec = new VirtualDeviceConfigSpec();

            diskSpec.fileOperation = VirtualDeviceConfigSpecFileOperation.create;
            diskSpec.fileOperationSpecified = true;
            diskSpec.operation = VirtualDeviceConfigSpecOperation.add;
            diskSpec.operationSpecified = true;

            VirtualDisk disk = new VirtualDisk();
            VirtualDiskFlatVer2BackingInfo diskfileBacking = new VirtualDiskFlatVer2BackingInfo();

            diskfileBacking.fileName = volumeName;
            diskfileBacking.diskMode = "persistent";

            disk.key = 0;
            disk.controllerKey = diskCtlrKey;
            disk.unitNumber = 0;
            disk.backing = diskfileBacking;
            disk.capacityInKB = diskSizeMB;
            disk.controllerKeySpecified = true;
            disk.unitNumberSpecified = true;
            
            diskSpec.device = disk;

            return diskSpec;
        }
    }
}
